package com.chuangke.login.service.impl;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.chuangke.admin.entity.SysUserRole;
import com.chuangke.admin.service.SysRoleService;
import com.chuangke.admin.service.SysUserRoleQueryService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Service;

import com.chuangke.admin.entity.SysDept;
import com.chuangke.admin.entity.SysUser;
import com.chuangke.admin.service.SysDeptService;
import com.chuangke.admin.service.SysUserService;
import com.chuangke.common.constant.AppMenuConstants;
import com.chuangke.common.constant.SysConfigConstants;
import com.chuangke.common.exception.ChuangkeException;
import com.chuangke.common.utils.AESUtil;
import com.chuangke.common.utils.CaptchaUtils;
import com.chuangke.common.utils.ReflectionUtils;
import com.chuangke.context.service.ContextService;
import com.chuangke.framework.service.RetryService;
import com.chuangke.login.common.LoginType;
import com.chuangke.login.config.LoginConfig;
import com.chuangke.login.dto.LoginBean;
import com.chuangke.login.plugin.LoginSecurityPlus;
import com.chuangke.security.authentication.CommonAuthenticatioToken;
import com.chuangke.security.authentication.UserPassAuthenticatioToken;
import com.chuangke.security.service.JwtTokenManager;
import com.chuangke.security.userdetails.JwtUserDetails;
import com.chuangke.system.manage.SystemConfigManager;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;

import cn.hutool.core.io.IoUtil;

@Service
public class SysLoginServiceImpl implements com.chuangke.login.service.SysLoginService {

	private static final Logger logger = LoggerFactory.getLogger(SysLoginServiceImpl.class);

	@Autowired
	private SysUserService sysUserService;
	@Autowired
	private SysDeptService sysDeptService;
	@Autowired
	private SysUserRoleQueryService sysUserRoleQueryService;
	@Autowired
	private AuthenticationManager authenticationManager;
	@Autowired
	private SystemConfigManager systemConfigManager;
	@Autowired
	private LoginSecurityPlus loginSecurityPlus;
	@Autowired
	private JwtTokenManager jwtTokenManager;
	@Autowired
	private RetryService retryService;
	@Autowired
	private LoginConfig loginConfig;
	@Autowired
	private ContextService contextService ;

	@Override
	public void captcha(HttpServletResponse response, HttpServletRequest request) throws IOException {
		response.setHeader("Cache-Control", "no-store, no-cache");
		response.setContentType("image/jpeg");

		String captchaSessionId = request.getSession().getId();
		String captchaLength = retryService.getRetryTimes(captchaSessionId) >= 2 ? "6" : "4";

		Producer producer = CaptchaUtils.getProducer(captchaLength);

		// 生成文字验证码
		String text = producer.createText();
		// 生成图片验证码
		BufferedImage image = producer.createImage(text);
		// 保存到验证码到 session
		request.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY, text);

		ServletOutputStream out = response.getOutputStream();
		ImageIO.write(image, "jpg", out);
		IoUtil.close(out);
	}

	@Override
	public Map<String, Object> login(LoginBean loginBean, HttpServletRequest request) {
		LoginType loginType = loginBean.getLoginType() == null ? LoginType.pass : loginBean.getLoginType();
//		loginType = LoginType.pass ;//登录暂时只支持密码，其他有需要放开
		String code = loginBean.getCode();

		CommonAuthenticatioToken providerToken;
		switch (loginType) {
		case pass:
			String username = loginBean.getAccount();
			String password = AESUtil.desEncrypt(loginBean.getPassword());
			String inputCaptcha = loginBean.getCaptcha();

			loginSecurityPlus.checkLoginRetryTimes(username);

			providerToken = new UserPassAuthenticatioToken(username, password, inputCaptcha, request);
			break;
		case dingtalk:
			code = code.replaceAll("#", "").replaceAll("/", "");
			logger.info("钉钉code:{},处理好的code:{}", loginBean.getCode(), code);
//			providerToken = new DingtalkAuthenticationToken(null, code);
			providerToken = ReflectionUtils.getInstance("com.chuangke.dingtalk.security.DingtalkAuthenticationToken", null, code) ;
			break;
		case weixin:
			logger.info("微信code:{}", loginBean.getCode());
//			providerToken = new WeixinAuthenticationToken(null, code);
			providerToken = ReflectionUtils.getInstance("com.chuangke.wx.security.WeixinAuthenticationToken", null, code) ;
			break;
		case matrix:
			logger.info("经纬code:{}", loginBean.getCode());
//			providerToken = new MatrixAuthenticationToken(null, code);
			providerToken = ReflectionUtils.getInstance("com.chuangke.matrix.security.MatrixAuthenticationToken", null, code) ;
			break;
		case sinopecotp:
			username = loginBean.getAccount();
			password = AESUtil.desEncrypt(loginBean.getPassword());
			String otp = loginBean.getOtp() ;
			providerToken = ReflectionUtils.getInstance("com.chuangke.sinopec.security.SinopecAuthenticationToken", username, password, otp) ;
			break;
		default:
			throw new ChuangkeException("未支持的登录方式：" + loginType);
		}

		// 初始化用户信息
		return doLogin(request, loginBean, providerToken);
	}
	
	private Map<String, Object> doLogin(HttpServletRequest request, LoginBean loginBean,
			CommonAuthenticatioToken providerToken) {
		CommonAuthenticatioToken authentication;
		String username = loginBean.getAccount();
		// 系统登录认证
		try {
			providerToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
			// 执行登录认证过程
			authentication = (CommonAuthenticatioToken) authenticationManager.authenticate(providerToken);
			// 认证成功存储认证信息到上下文
			SecurityContextHolder.getContext().setAuthentication(authentication);
			// 生成令牌并返回给客户端
			authentication.setToken(jwtTokenManager.generateToken(authentication));

			retryService.retrySuccessed(request.getSession().getId());
			loginSecurityPlus.loginSuccessed(username);

			JwtUserDetails ud = (JwtUserDetails) authentication.getPrincipal();

			username = ud.getUser().getName();

			logoutOtherLoginedUser(authentication, username);
		} catch (Exception e) {
			retryService.retry(request.getSession().getId());
			loginSecurityPlus.loginFailed(username);
			throw e;
		}

		return getLoginInfo(username, authentication);
	}

	private Map<String, Object> getLoginInfo(String username, CommonAuthenticatioToken authentication) {
		Map<String, Object> loginInfo = new HashMap<>();

		SysUser user = sysUserService.findByName(username);
		SysDept sysDept = sysDeptService.findById(user.getDeptId());
		List<SysUserRole> userRoles = sysUserRoleQueryService.findByUserId(user.getId());

		loginInfo.put("userId", user.getId());
		loginInfo.put("name", user.getName());
		loginInfo.put("nickName", user.getNickName());
		loginInfo.put("postId", user.getPostId());
		loginInfo.put("jobType", user.getJobType());
		loginInfo.put("cardId", user.getCardId());
		loginInfo.put("hrId", user.getHrId());
		loginInfo.put("userDeptId", user.getDeptId());
		loginInfo.put("userDeptName", user.getDeptName());
		loginInfo.put("userDeptType", sysDept != null ? sysDept.getDeptType() : SysDept.DEPT_TYPE_JCDW);

		//返回前台权限用于控制按钮显示
		loginInfo.put("authorityRoleList", userRoles.stream().map(SysUserRole::getRoleCode).collect(Collectors.toList()));

		loginInfo.put("avatar", user.getAvatar());
		loginInfo.put("sysRouter", systemConfigManager.getRouter());
		loginInfo.put("isAdmin", systemConfigManager.isAdminRole());
		loginInfo.put("authorities", authentication.getAuthorities());
		loginInfo.put("token", authentication.getToken());
		loginInfo.put("loginMsg", "");// 登录提示
		
		loginInfo.put("homeType", getHomeType()) ;

		loginInfo.put("compId", contextService.getCurrentCompId());// 所属单位

		loginInfo.put("appMenu", getAppMenu());
		if(authentication.getPrincipal()!=null){
			JwtUserDetails userDetails = (JwtUserDetails) authentication.getPrincipal() ;
			if (userDetails.getUser()!=null) {
				loginInfo.put("classification",userDetails.getUser().getStra() );// 密级
			}
		}

		return loginInfo;
	}
	
	private String getHomeType() {
		if(systemConfigManager.isLeaderRole()) {
			return "leader" ;
		}
		List<String> userRoleList = contextService.getCurrentRoleIds() ;
		
		if(userRoleList.contains("task_xxy")) {
			return "task_xxy" ;
		}
		return "normal" ;
	}

	public List<String> getAppMenu() {
		Map<String, List<String>> roleMenuMap = new HashMap<>();
		roleMenuMap.put(SysConfigConstants.APP_CINFIG_ADMIN_ROLES, Arrays.asList(AppMenuConstants.list,
				AppMenuConstants.cgrk, AppMenuConstants.bfcz, AppMenuConstants.dczl));

		roleMenuMap.put(SysConfigConstants.APP_CINFIG_DEPT_ADMIN_ROLES,
				Arrays.asList(AppMenuConstants.ly, AppMenuConstants.tk, AppMenuConstants.sj, AppMenuConstants.lj,
						AppMenuConstants.list, AppMenuConstants.dczl));

		roleMenuMap.put(SysConfigConstants.APP_CINFIG_NORMAL_ROLES, Arrays.asList(AppMenuConstants.sj, AppMenuConstants.lj,
				AppMenuConstants.rj, AppMenuConstants.cj, AppMenuConstants.list, AppMenuConstants.dczl,
				AppMenuConstants.cgrk, AppMenuConstants.bfcz));

		List<String> appRoleTypes = systemConfigManager.getAppRoleTypeList();

		List<String> appMenus = new ArrayList<>();
		if (CollectionUtils.isEmpty(appRoleTypes)) {
			return appMenus;
		}
		
		appRoleTypes.forEach(a -> appMenus.addAll(roleMenuMap.get(a)));

		return appMenus;
	}

	private void logoutOtherLoginedUser(CommonAuthenticatioToken authentication, String username) {
		if (!loginConfig.isOneTimeOnlyOneLogin()) {
			return;
		}

		List<String> loginedUuidList = jwtTokenManager.getOtherLoginUuid(username, authentication.getToken());
		if (!CollectionUtils.isEmpty(loginedUuidList)) {
			authentication.setLoginMsg("你的账号目前已在其他设备登录，系统已强制退出其他设备的登录");
			loginedUuidList.forEach(uuid -> jwtTokenManager.delLoginUser(uuid));
		}
	}

	@Override
	public void logout(HttpServletRequest request) {
		JwtUserDetails loginUser = jwtTokenManager.getLoginUser(request);
		if (loginUser != null) {
			jwtTokenManager.delLoginUser(loginUser.getUuid());
		}
	}

}
